<link rel="import" href="../../bower_components/polymer/polymer-element.html">
<link rel="import" href="../../bower_components/paper-card/paper-card.html">
<link rel="import" href="../../bower_components/paper-toast/paper-toast.html">
<link rel="import" href="../../bower_components/iron-ajax/iron-ajax.html">
<link rel="import" href="../../bower_components/iron-icons/iron-icons.html">
<link rel="import" href="../../bower_components/app-route/app-route.html">
<link rel="import" href="../../bower_components/app-route/app-location.html">
<link rel="import" href="../../bower_components/paper-dropdown-menu/paper-dropdown-menu.html">
<link rel="import" href="../../bower_components/paper-item/paper-item.html">
<link rel="import" href="../../bower_components/paper-listbox/paper-listbox.html">
<link rel="import" href="../../bower_components/neon-animation/web-animations.html">

<link rel="import" href="build-card.html">
<link rel="import" href="shared-styles.html">

<dom-module id="build-stats">
    <template>
        <style include="shared-styles"></style>
        <style is="custom-style">
            :host {
                display: block;
                width: 100%;
                height: 164px;
                text-align: center;
            }

            paper-card {
                width: 100%;
                height: 100%;
            }

            .joke {
                font-size: 0.8em;
                opacity: 0.8;
            }

            .date {
                position: absolute;
                font-size: 0.8em;
                opacity: 0.8;
                top: 6px;
                right: 6px;
            }

            .config {
                position: absolute;
                top: 6px;
                left: 6px;
                color: var(--primary-color);
                cursor: pointer;
            }

            paper-item {
                cursor: pointer;
            }

            #config {
                text-align: left;
                width: 100%;
            }

            #config td, #config th {
                border: 1px solid rgba(255, 255, 255, 0.3);
                padding: 8px;
            }

            th {
                color: var(--primary-color);
            }

            .icon {
                margin-left: 6px;
            }

            li {
                text-align: left;
            }

            #filters {
                bottom: 0px;
                position: absolute;
                margin: auto;
                width: 100%;
                display: flex;
                justify-content: space-between;
            }

            #filter-repo {
                margin-left: 8px;
            }

            #filter-branch {
                margin-right: 8px;
            }

        </style>

        <app-location route="{{route}}" use-hash-as-path></app-location>
        <app-route route="{{route}}" pattern="/:repository/:branch" data="{{routeData}}" tail="{{subroute}}"></app-route>

        <paper-card elevation="1">
            <h4>Zapperfly build server 1.1.1</h4>

            <span class="joke">[[joke]]</span>
            <span class="date">[[time]]</span>

            <div class="config">
                <template is="dom-if" if="[[authenticated]]">
                    <iron-icon on-click="_configure" class="icon" icon="icons:settings"></iron-icon>
                </template>
                <template is="dom-if" if="[[!authenticated]]">
                    <iron-icon on-click="_loginDialog" class="icon" icon="icons:power-settings-new"></iron-icon>
                </template>
                <iron-icon on-click="_about" class="icon" icon="icons:info-outline"></iron-icon>
            </div>

            <!-- todo get values, todo include reset/any.-->

            <div id="filters">
                <!-- repository filter -->
                <paper-dropdown-menu id="filter-repo" label="Repository" on-iron-select="_onRepoChanged">
                    <paper-listbox id="repositories" slot="dropdown-content" attr-for-selected="name"
                                   selected="{{routeData.repository}}">

                        <paper-item name="any">any</paper-item>

                        <template id="repositoryTemplate" is="dom-repeat" items="[[repositories]]">
                            <paper-item name="[[item]]">[[item]]</paper-item>
                        </template>
                    </paper-listbox>
                </paper-dropdown-menu>

                <!-- branch filter -->
                <paper-dropdown-menu id="filter-branch" label="Branch" on-iron-select="_onBranchChanged">
                    <paper-listbox id="branches" slot="dropdown-content" attr-for-selected="name"
                                   selected="{{routeData.branch}}">

                        <paper-item name="any">any</paper-item>

                        <template id="branchTemplate" is="dom-repeat" items="[[branches]]">
                            <paper-item name="[[item]]">[[item]]</paper-item>
                        </template>
                    </paper-listbox>
                </paper-dropdown-menu>
            </div>

        </paper-card>

        <paper-dialog id="about" with-backdrop>
            <a target="_blank" href="https://github.com/codingchili/zapperfly-asm"><img
                    style="position: absolute; top: 0; right: 0; border: 0;"
                    src="https://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png"
                    alt="Fork me on GitHub"></a>
            <h2>About</h2>
            <paper-dialog-scrollable style="margin-top:64px;">

                <p>
                    Zapperfly-asm is a clustered build server with minimal setup!
                    Zapperfly is developed by Robin Duda, open source and available on github.
                </p>

                <hr>
                CHANGELOG
                <br>

                1.1.1
                <ul>
                    <li>when running the command to add users, the password was not being saved.</li>
                    <li>commandline was not parsed correctly for unix / docker builds.</li>
                    <li>use absolute paths when mounting a volume in docker. (support newer docker version)</li>
                    <li>logging events are now lowercase separated with dot, example: 'buildComplete' -> 'build.complete'.</li>
                </ul>

                <hr>
                1.1.0
                <ul>
                    <li>support for plugins.</li>
                    <li>new plugin: update bitbucket with build status.</li>
                    <li>new plugin: notifyCommit API that matches the jenkins plugin.</li>
                    <li>now displaying the commit author on build cards.</li>
                    <li>support for cancelling builds.</li>
                    <li>support for scheduling builds from the logger.</li>
                    <li>improved log scrolling: pauses auto scroll when not at scrollTop</li>
                    <li>now displaying the commit author on build cards.</li>
                    <li>remembers the login token across page reloads.</li>
                    <li>server side filters to filter on repository and branch.</li>
                    <li>client side filters to filter on author, commit message, etc.</li>
                    <li>added hot-linking to builds and a copy url to clipboard.</li>
                    <li>no longer need a repo/branch to run shell scripts.</li>
                    <li>display which user that scheduled a build in the queue.</li>
                    <li>possible to setup multiple configurations for a single branch.</li>
                </ul>

                <hr>
                1.0.4
                <ul>
                    <li>fix content type for requests to allow double ampersand.</li>
                    <li>fixed a decoding error on the server for the log offset.</li>
                    <li>changed the default builds directory to be relative,</li>
                </ul>

                <hr>
                1.0.3
                <ul>
                    <li>login dialog and logout button.</li>
                    <li>role based access control: read/start/configure.</li>
                    <li>commandline to add new users to a running system.</li>
                    <li>allow cloning with ssh and using basic authentication in repo url.</li>
                    <li>added this changelog to the about dialog.</li>
                    <li>adjusted the size of buttons to be more appealing.</li>
                    <li>renamed the startup script from run -> zapperfly to avoid "run --start".</li>
                </ul>

                <hr>
                1.0.2
                <ul>
                    <li>now support for building in containers!</li>
                    <li>use a hazelcast queue to schedule builds over the cluster.</li>
                    <li>use a hazelcast list to store builds logs.</li>
                    <li>added input validation for build configuration.</li>
                    <li>split the queue list API from the build list API.</li>
                    <li>support newline in logs and improved grouping of log messages.</li>
                    <li>support building without passing the buildscript over the shell (uses a file for docker
                        builds)
                    </li>
                    <li>fixed - full build output was not captured.</li>
                </ul>
                <hr>
                1.0.1
                <ul>
                    <li>added a distributed hazelcast semaphore used when taking jobs from the queue.</li>
                    <li>mark all builds attached to an instance as failed if the owning instance goes down.</li>
                    <li>show executors cpu/memory usage and "last seen" when instance goes offline.</li>
                    <li>improved API latency worst case scenario from 250ms -> 5ms. (cluster routing)</li>
                    <li>added the ability to remove builds that are no longer running from history.</li>
                    <li>refactored the API into 'builds' and 'configuration'.</li>
                    <li>improved the UI, new color theme, various fixes and improvements including mobile/PWA</li>
                    <li>fixed an issue with autoclean not deleting readonly files: now adds write before deleting.</li>
                    <li>fixed styling issues with paper-input and black text.</li>
                    <li>fixed styling issues with paper-dialogs, the build log and checkboxes..</li>
                </ul>
                <hr>
                1.0.0
                <ul>
                    <li>schedule builds across the cluster (clone + build)</li>
                    <li>website (run with --website param) and API.</li>
                    <li>real-time logging output on the website.</li>
                    <li>support for setting instance capacity and name.</li>
                    <li>support for grouping instances with --group parameter.</li>
                    <li>configure builds by git repository url and branches.</li>
                    <li>clustered build data and logs.</li>
                    <li>full mobile support and may be added to homescreen as a progressive web app.</li>
                </ul>

            </paper-dialog-scrollable>
            <div class="buttons">
                <paper-button raised dialog-dismiss>Close</paper-button>
            </div>
        </paper-dialog>

        <paper-dialog id="login" with-backdrop>
            <h2>Login</h2>
            <paper-dialog-scrollable>

                <paper-input-container>
                    <label slot="label">username</label>
                    <iron-input slot="input" on-keydown="_submitLogin" bind-value="{{username}}">
                        <input autofocus>
                    </iron-input>
                </paper-input-container>

                <paper-input-container>
                    <label slot="label">password</label>
                    <iron-input slot="input" on-keydown="_submitLogin" bind-value="{{password}}">
                        <input type="password">
                    </iron-input>
                </paper-input-container>

            </paper-dialog-scrollable>
            <div class="buttons">
                <paper-button raised on-click="_login">Login</paper-button>
            </div>
        </paper-dialog>

        <paper-dialog id="configure" with-backdrop>
            <iron-icon on-click="_logout" class="icon" icon="icons:power-settings-new"></iron-icon>
            <h2>Configuration</h2>
            <paper-dialog-scrollable>

                <span style="opacity:0.45">cluster-wide settings.</span>

                <table id="config">
                    <tr>
                        <th>Key</th>
                        <th>Value</th>
                    </tr>
                    <tr>
                        <td>User name</td>
                        <td>[[user]]</td>
                    </tr>
                    <tr>
                        <td>User role</td>
                        <td>[[role]]</td>
                    </tr>
                    <tr>
                        <td>Cluster group</td>
                        <td>[[config.groupName]]</td>
                    </tr>
                    <tr>
                        <td>Webserver host</td>
                        <td>[[config.instanceName]]</td>
                    </tr>
                    <tr>
                        <td>Build timeout</td>
                        <td>[[config.timeoutSeconds]] seconds.</td>
                    </tr>
                    <tr>
                        <td>Build path</td>
                        <td>[[config.buildPath]]</td>
                    </tr>
                </table>
            </paper-dialog-scrollable>
            <div class="buttons">
                <paper-button raised dialog-dismiss>Close</paper-button>
            </div>
        </paper-dialog>

        <iron-ajax auto id="jokeLoader" url="https://api.icndb.com/jokes/random?limitTo=[nerdy]" handle-as="json"
                   on-response="_onJokes">
        </iron-ajax>

        <iron-ajax id="configLoader" method="post" url="/api/" handle-as="json"
                   on-response="_onConfig" debounce-duration="300">
        </iron-ajax>

        <iron-ajax id="filterLoader" method="post" url="/api/?target=config&route=filters" handle-as="json"
                   on-response="_onFilters" debounce-duration="300" auto>
        </iron-ajax>

        <iron-ajax id="loginRequest"
                   url="/api/?target=authentication&route=login&username=[[username]]&password=[[password]]"
                   handle-as="json"
                   on-response="_onLogin">
        </iron-ajax>

        <paper-toast id="toast" text="[[errorText]]"></paper-toast>

    </template>
</dom-module>
<script>
    class BuildStats extends Polymer.Element {

        static get is() {
            return 'build-stats';
        }

        constructor() {
            super();

            this.username = '';
            this.password = '';
            this.authenticated = false;

            this._updateTime();
            setInterval(() => {
                this._updateTime();
            }, 1000);

            setInterval(() => {
                this.$.jokeLoader.generateRequest();
            }, 10000);
        }

        ready() {
            super.ready();
            if (!this.route.path) {
                this.set('route.path', "/any/any/");
            }

            application.onAuthenticated(() => {
                this._load();
            });

            this._onResumeSession();
        }

        _about() {
            this.$.about.open();
        }

        _logout() {
            localStorage.removeItem('authentication');
            this.authenticated = false;
            this.$.configure.close();
            application.logout();
        }

        _loginDialog() {
            this.$.login.open();
        }

        _login() {
            this.$.loginRequest.generateRequest();
        }

        _submitLogin(e) {
            if (e.keyCode === 13) {
                this._login();
            }
        }

        _onResumeSession() {
            let authentication = JSON.parse(localStorage.getItem('authentication'));
            if (authentication) {
                this._setAuthenticated(authentication);
            }
        }

        _onLogin(event, request) {
            if (request.response.status === ACCEPTED) {
                this._setAuthenticated(request.response);
                localStorage.setItem('authentication', JSON.stringify(request.response));
                this.$.login.close();
            } else {
                this.password = '';
                this.errorText = request.response.message;
                this.$.toast.open();
            }
        }

        _setAuthenticated(response) {
            application.authenticated(response);
            this.authenticated = true;
            this.user = application.token.domain;
            this.role = application.token.properties['role'];
        }

        _configure() {
            this._load();
            this.$.configure.open();
        }

        _load() {
            this.$.configLoader.body = JSON.stringify({
                token: application.token,
                target: 'config',
                route: 'cluster'
            });
            this.$.configLoader.generateRequest();
        }

        _onFilters(event, request) {
            this.filter = request.response.repositories;
            if (this.filter) {
                this.set('repositories', Object.keys(this.filter));
            }
        }

        _onRepoChanged(e) {
            let repository = e.target.selectedItem.innerText;
            this.set('repository', repository);
            this.set('branch', 'any');
            if (this.filter) {
                this.set('branches', this.filter[repository]);
                this._requestHistoryUpdate();
            }
        }

        _onBranchChanged(e) {
            this.set('branch', e.target.selectedItem.innerText);
            this._requestHistoryUpdate();
        }

        _requestHistoryUpdate() {
            application.publish('onFiltersChanged', {
                repository: this.repository,
                branch: this.branch
            });
        }

        _updateTime() {
            this.time = new Date().toLocaleString();
        }

        _onJokes(event, request) {
            this.joke = request.response.value.joke;
        }

        _onConfig(event, request) {
            this.config = request.response;
        }
    }

    window.customElements.define(BuildStats.is, BuildStats);
</script>
